package com.timedomain.him.remake.utils;

import android.content.Context;
import com.google.gson.Gson;
import com.timedomain.him.remake.Program;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Pattern;

public class DataPackManager {

    public static final String OfficialDataPackId = "official_update";
    public static final int OfficialDataPackBuildinVersion = 23110301;

    @FunctionalInterface
    public interface OnDataPackReloadListener{
        void onDataPackReload(Context ctx);
    }

    private static List<OnDataPackReloadListener> callbacks = new ArrayList<>();

    private static List<DataPackMetadata> installedDataPacks = new ArrayList<>();

    public static List<DataPackMetadata> getInstalledDataPacks(){
        return installedDataPacks;
    }

    private static DataPackMetadata officialReleaseDataPack = null;

    public static DataPackMetadata getOfficialReleaseDataPack(){
        if(officialReleaseDataPack == null){
            DataPackMetadata metadata = new DataPackMetadata();
            metadata.version = OfficialDataPackBuildinVersion;
            metadata.id = OfficialDataPackId;
            metadata.name = "[内置数据包]";
            metadata.description = "他·温暖";
            return metadata;
        }
        return officialReleaseDataPack;
    }

    public static void registerOnDatapackLoad(OnDataPackReloadListener callback){
        if(callbacks == null){callbacks = new ArrayList<>();}
        callbacks.add(callback);
    }

    public static File getDataPackDir(String dataPackId){
        return new File(externalDataPackRootDir,dataPackId);
    }

    static File externalDataPackRootDir = null;

    private static void loadDefaultDataPack(Context ctx){
        Thread loadThread = new Thread(() -> {
            HashMap<String, DataPackEntry> dataPackMapping = new HashMap<>();
            List<DataPackEntry> dataPackEntryList = new ArrayList<>();
            try(InputStream inputStream = ctx.getAssets().open("files.txt")){
                try(BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))){
                    String line = null;
                    while ((line = br.readLine())!=null){
                        line = line.trim();
                        if(line.length() > 1) {
                            String[] args = line.split(Pattern.quote(" "),2);
                            String path = args[1].trim();
                            String hash = args[0].trim();
                            DataPackEntry entry = new DataPackEntry(path,hash,true);
                            dataPackEntryList.add(entry);
                            dataPackMapping.put(path,entry);
                        }
                    }
                }
                DataPackManager.dataPackMapping = dataPackMapping;
                DataPackManager.dataPackEntryList = dataPackEntryList;
            }catch (IOException ex){
                HLog.e("无法加载数据包：",ex.toString());
                ex.printStackTrace();
                //throw new AndroidRuntimeException("Error while loading datapack",ex);
            }
        });

        try {
            loadThread.start();
            loadThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void loadExternalDataPack(){
        Thread loadThread = new Thread(() -> {
            HLog.w("加载数据包","开始加载外部数据包");
            try{
                List<Map.Entry<String,DataPackEntry>> toBeAdded = new ArrayList<>();
                List<DataPackMetadata> dataPackMetadataList = new ArrayList<>();
                DataPackMetadata officialDataPack = null;
                if(!externalDataPackRootDir.exists()){
                    externalDataPackRootDir.mkdirs();
                }
                var files = externalDataPackRootDir.listFiles();
                if(files == null){files = new File[0];}
                for (File file: files) {

                    HLog.w("加载数据包","正在加载："+file.getName());
                    if(file.isDirectory()){
                        List<Map.Entry<String,DataPackEntry>> dataPackContents = new ArrayList<>();
                        DataPackMetadata dataPackMetadata = loadOneDataPack(file,dataPackContents);
                        if(dataPackMetadata != null){
                            if(dataPackMetadata.id.equals(OfficialDataPackId) && dataPackMetadata.version < OfficialDataPackBuildinVersion){
                                continue;
                            }
                            toBeAdded.addAll(dataPackContents);
                            if(dataPackMetadata.id.equals(OfficialDataPackId)){
                                officialDataPack = dataPackMetadata;
                            }
                            else{
                                dataPackMetadataList.add(dataPackMetadata);
                            }
                        }

                    }
                }
                for (var item :
                        toBeAdded) {
                    dataPackMapping.remove(item.getKey());
                    dataPackMapping.put(item.getKey(),item.getValue());
                    HLog.d("Extern datapack file",item.getKey() + " -> " + item.getValue().hash);
                }
                dataPackEntryList = new ArrayList<>(dataPackMapping.values());
                Collections.sort(dataPackEntryList,(a1,a2)->a1.path.compareToIgnoreCase(a2.path));
                installedDataPacks = dataPackMetadataList;
                officialReleaseDataPack = officialDataPack;
            }catch (Exception ex){
                HLog.e("无法加载数据包：",ex.toString());
                ex.printStackTrace();
            }
        });

        try {
            loadThread.start();
            loadThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }



    private static DataPackMetadata loadOneDataPack(File dir,List<Map.Entry<String,DataPackEntry>> dest){
        String metaFile = "meta.json";
        Gson gson = new Gson();
        try{
            DataPackMetadata dataPackMetadata = gson.fromJson(PhysicalFileUtils.readAllText(new File(dir,metaFile)), DataPackMetadata.class);
            if(!dataPackMetadata.id.equals(dir.getName())){
                throw new IllegalStateException("损坏的数据包：数据包ID和文件夹不匹配");
            }
            enumerateExternalDataPackDirectories(new File(dir,"data"),"",dest);
            return dataPackMetadata;
        }catch (Exception ex){
            HLog.w("载入数据包错误",dir.getName());
            ex.printStackTrace();
        }
        return null;
    }

    private static void enumerateExternalDataPackDirectories(File dir,String currentDirectory,List<Map.Entry<String,DataPackEntry>> dest){
        for (File f:
             dir.listFiles()) {
            if(f.isDirectory()){
                enumerateExternalDataPackDirectories(f,currentDirectory+f.getName()+"/",dest);
            }
            else{
                DataPackEntry dpe = new DataPackEntry(currentDirectory+f.getName(),f.getAbsolutePath(),false);
                dest.add(new AbstractMap.SimpleEntry<>(dpe.path,dpe));

            }
        }
    }

    public static HashMap<String,DataPackEntry> getDataPack(Context ctx){
        return dataPackMapping;
    }

    public static List<DataPackEntry> getDataEntries(Context ctx){
        return dataPackEntryList;
    }
    
    private static void invokeDatapackReloadEvent(Context ctx){

        if(callbacks != null){
            for (OnDataPackReloadListener item :
                    callbacks) {
                item.onDataPackReload(ctx);
            }
        }
    }

    public static void notifyDataPackChanged(Context ctx){
        HLog.w("加载数据包","重新加载所有数据包中...");
        loadExternalDataPack();
        invokeDatapackReloadEvent(ctx);
    }

    public static void initDataPackSystem(Context ctx){
        externalDataPackRootDir = new File(ctx.getFilesDir(),"datapacks");
        loadDefaultDataPack(ctx);
        loadExternalDataPack();
    }


    private static HashMap<String,DataPackEntry> dataPackMapping = null;
    private static List<DataPackEntry> dataPackEntryList = null;


    public static class DataPackEntry{
        public String path;

        public String prefix;
        public String hash;

        public DataPackEntry(String path, String hash, boolean isBuildin) {
            this.path = path;
            this.prefix = isBuildin ? "@" : "~";
            this.hash = hash;
        }

    }

    public static class DataPackMetadata{

        public String id = "";
        public String name = "";
        public int version = -1;
        public String description = "";

        public int minAppVersion = -1;

    }

}
